#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.IO;

namespace AbstractClass.Security.Cryptography
{
    ///<summary>
    /// Computes the CRC32 hash of the input data.
    ///</summary>
    public sealed class Crc32 : IDisposable
    {
        private const int BufferSize = 1024;
        private static uint[] _crc32Table;
        private byte[] _buffer;
        private bool _disposed;


        /// <summary>
        ///   Initializes a new instance of the <see cref = "Crc32" /> class.
        /// </summary>
        public Crc32()
        {
            unchecked
            {
                const uint dwPolynomial = 0xEDB88320;
                uint i;

                _crc32Table = new uint[256];

                for (i = 0; i < 256; i++)
                {
                    uint dwCrc = i;
                    for (uint j = 8; j > 0; j--)
                    {
                        if ((dwCrc & 1) == 1)
                        {
                            dwCrc = (dwCrc >> 1) ^ dwPolynomial;
                        }
                        else
                        {
                            dwCrc >>= 1;
                        }
                    }
                    _crc32Table[i] = dwCrc;
                }
            }
        }

        #region IDisposable Members

        /// <summary>
        ///   Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        /// <summary>
        /// Computes the hash value for the specified <see cref="Stream"/> object.
        /// </summary>
        /// <param name="stream">The input stream.</param>
        /// <returns>The computed hash code.</returns>
        [CLSCompliant(false)]
        public UInt32 ComputeHash(Stream stream)
        {
            unchecked
            {
                uint crc32Result = 0xFFFFFFFF;
                _buffer = new byte[BufferSize];
                const int readSize = BufferSize;

                int count = stream.Read(_buffer, 0, readSize);
                while (count > 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        crc32Result = ((crc32Result) >> 8) ^ _crc32Table[(_buffer[i]) ^ ((crc32Result) & 0x000000FF)];
                    }
                    count = stream.Read(_buffer, 0, readSize);
                }
                return ~crc32Result;
            }
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    if (_crc32Table != null)
                    {
                        Array.Clear(_crc32Table, 0, _crc32Table.Length);
                    }
                    _crc32Table = null;

                    if (_buffer != null)
                    {
                        Array.Clear(_buffer, 0, _buffer.Length);
                    }
                    _buffer = null;
                }

                _disposed = true;
            }
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="Crc32"/> is reclaimed by garbage collection.
        /// </summary>
        ~Crc32()
        {
            Dispose(false);
        }
    }
}